home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / dte5_1.zip / HWIBMCGA.C < prev    next >
C/C++ Source or Header  |  1991-02-06  |  39KB  |  1,285 lines

  1. /*
  2.  * Written by Douglas Thomson (1989/1990)
  3.  *
  4.  * This source code is released into the public domain.
  5.  */
  6.  
  7. /*
  8.  * Name:    dte - Doug's Text Editor program - hardware dependent module
  9.  * Purpose: This file contains all the code that needs to be different on
  10.  *           different hardware.
  11.  * File:    hwibmcga.c
  12.  * Author:  Douglas Thomson
  13.  * System:  This particular version is for the IBM PC and close compatibles.
  14.  *           It uses Turbo C's screen output functions, which means there
  15.  *           should be no problem with "snow" on CGA cards. See the file
  16.  *           "hwibm.c" for a faster version that writes directly to the
  17.  *           video RAM.
  18.  *          This version also supports a command line option to simulate
  19.  *           slow baud rates, so that a PC user can get some idea of how
  20.  *           the editor would perform across a slow serial line. This
  21.  *           feature was probably more useful to me in designing screen
  22.  *           update algorithms, but may be of interest to others also...
  23.  * Date:    October 10, 1989
  24.  * Notes:   This module has been kept as small as possible, to facilitate
  25.  *           porting between different systems.
  26.  */
  27. #include "common.h"     /* dte types */
  28. #include "hwdep.h"      /* prototypes for functions here */
  29. #include "utils.h"      /* for displaying messages etc */
  30. #include "version.h"    /* current version number */
  31. #include <stdarg.h>     /* for passing variable numbers of arguments */
  32. #include <conio.h>      /* for using putch to output a character */
  33. #include <dos.h>        /* for renaming files */
  34. #include <dir.h>        /* for searching the current path */
  35. #include <bios.h>       /* for direct BIOS keyboard input */
  36. #include <alloc.h>      /* for memory allocation */
  37. #include <io.h>         /* for file attribute code */
  38. #include <fcntl.h>      /* open flags */
  39. #include <process.h>    /* spawn etc */
  40. #include <sys/stat.h>   /* S_IWRITE etc */
  41.  
  42. /*
  43.  * prototypes for all functions in this file
  44.  */
  45. void error ARGS((int kind, ...));
  46. void main ARGS((int argc, char *argv[]));
  47. static void sim_delay ARGS((int n));
  48. static void att_stuff ARGS((void));
  49. void att_check ARGS((void));
  50. void hw_xygoto ARGS((void));
  51. int hw_clreol ARGS((void));
  52. int hw_linedel ARGS((int line));
  53. int hw_scroll_up ARGS((int top, int bottom));
  54. int hw_lineins ARGS((int line));
  55. int hw_scroll_down ARGS((int top, int bottom));
  56. int hw_c_avail ARGS((void));
  57. int hw_c_input ARGS((void));
  58. void hw_c_output ARGS((int c));
  59. void hw_terminate ARGS((void));
  60. void hw_initialize ARGS((void));
  61. void hw_move ARGS((text_ptr dest, text_ptr source, long number));
  62. int hw_backspace ARGS((void));
  63. int hw_c_insert ARGS((void));
  64. int hw_c_delete ARGS((void));
  65. int hw_rename ARGS((char *old, char *new));
  66. int hw_fattrib ARGS((char *name));
  67. int hw_set_fattrib ARGS((char *name, int attrib));
  68. int hw_unlink ARGS((char *name));
  69. int hw_printable ARGS((int c));
  70. int hw_load ARGS((char *name, text_ptr start, text_ptr limit, text_ptr *end));
  71. static int write_file ARGS((char *name, char *mode, text_ptr start,
  72.         text_ptr end));
  73. int hw_save ARGS((char *name, text_ptr start, text_ptr end));
  74. int hw_append ARGS((char *name, text_ptr start, text_ptr end));
  75. int hw_print ARGS((text_ptr start, text_ptr end));
  76. void hw_copy_path ARGS((char *old, char *name, char *new));
  77. int hw_os_shell ARGS((void));
  78.  
  79. /*
  80.  * Name:    error
  81.  * Purpose: To report an error, and usually make the user type <ESC> before
  82.  *           continuing.
  83.  * Date:    October 10, 1989
  84.  * Passed:  kind:   an indication of how serious the error was:
  85.  *                      TEMP:    merely a message, do not wait for <ESC>
  86.  *                      DIAG:    merely a message, but make sure user sees it
  87.  *                      WARNING: error, but editor can continue after <ESC>
  88.  *                      FATAL:   abort the editor!
  89.  *          format: printf format string for any arguments that follow
  90.  *          ...:    arguments to be printed
  91.  * Notes:   This function should be system independent; that is the whole
  92.  *           point of the "stdarg" philosophy. However, two of the systems
  93.  *           I have used implemented "stdarg" incompatibly, and some older
  94.  *           systems may not support the "stdarg" macros at all...
  95.  */
  96. void error(kind, format)
  97. int kind;
  98. char *format;
  99. {
  100.     va_list argptr;         /* used to access various arguments */
  101.     char buff[MAX_COLS];    /* somewhere to store error before printing */
  102.     int c;                  /* character entered by user to continue */
  103.  
  104.     /*
  105.      * prepare to process variable arguments
  106.      */
  107.     va_start(argptr, format);
  108.  
  109.     /*
  110.      * tell the user what kind of an error it is
  111.      */
  112.     switch (kind) {
  113.     case FATAL:
  114.         strcpy(buff, "Fatal error: ");
  115.         break;
  116.     case WARNING:
  117.         strcpy(buff, "Warning: ");
  118.         break;
  119.     case DIAG:
  120.     case TEMP:
  121.         strcpy(buff, "");
  122.         break;
  123.     }
  124.  
  125.     /*
  126.      * prepare the error message itself
  127.      */
  128.     vsprintf(buff + strlen(buff), format, argptr);
  129.     va_end(argptr);
  130.  
  131.     /*
  132.      * tell the user how to continue editing if necessary
  133.      */
  134.     if (kind == WARNING || kind == DIAG) {
  135.         strcat(buff, ": type <ESC>");
  136.     }
  137.  
  138.     /*
  139.      * output the error message
  140.      */
  141.     set_prompt(buff, 1);
  142.  
  143.     if (kind == FATAL) {
  144.         /*
  145.          * no point in making the user type <ESC>, since the program is
  146.          *  about to abort anyway...
  147.          */
  148.         terminate();
  149.         exit(1);
  150.     }
  151.     else if (kind != TEMP) {
  152.         /*
  153.          * If necessary, force the user to acknowledge the error by
  154.          *  typing <ESC> (or ^U).
  155.          * This prevents any extra commands the user has entered from
  156.          *  causing problems after an error may have made them inappropriate.
  157.          */
  158.         while ((c=c_input()) != 27 && c != CONTROL('U')) {
  159.             set_prompt(buff, 1);
  160.         }
  161.     }
  162. }
  163.  
  164. /*
  165.  * g_speed stores the number of milliseconds it takes to transmit a single
  166.  *  character across a simulated communication line.
  167.  * The default is to run at full speed. (My intuitive observation is that
  168.  *  on a 4.77 MHz IBM PC, "full speed" corresponds to about 4800 baud!)
  169.  */
  170. static int g_speed = 0;
  171.  
  172. /*
  173.  * Name:    harmless
  174.  * Purpose: To process control-break by ignoring it, so that the editor is
  175.  *           not aborted!
  176.  * Date:    February 5, 1990
  177.  */
  178. static int harmless(void)
  179. {
  180.     return 1;   /* ignore */
  181. }
  182.  
  183.  
  184. /*
  185.  * original control-break checking flag
  186.  */
  187. static int s_cbrk;
  188.  
  189. /*
  190.  * Name:    main
  191.  * Purpose: To do any system dependent command line argument processing,
  192.  *           and then call the main editor function.
  193.  * Date:    October 10, 1989
  194.  * Passed:  argc:   number of command line arguments
  195.  *          argv:   text of command line arguments
  196.  */
  197. void main(argc, argv)
  198. int argc;
  199. char *argv[];
  200. {
  201.     char drive[MAXDRIVE];  /* drive which dte.exe came from */
  202.     char dir[MAXDIR];      /* directory for dte.exe */
  203.  
  204.     /*
  205.      * trap control-break to make it harmless, and turn checking off
  206.      */
  207.     s_cbrk = getcbrk();
  208.     ctrlbrk(harmless);
  209.     setcbrk(0);
  210.  
  211.     /*
  212.      * set up help file name. This is a file called dte.hlp, and it should
  213.      *  be in the same directory as the dte.exe program.
  214.      * This information is only available in DOS 3 and later, so we need
  215.      *  to check to see whether argv[0] was OK.
  216.      */
  217.     if (fnsplit(argv[0], drive, dir, NULL, NULL) & DIRECTORY) {
  218.         fnmerge(g_status.help_file, drive, dir, "dte", ".hlp");
  219.     }
  220.  
  221.     /*
  222.      * see if the user specified a baud rate to simulate
  223.      */
  224.     if (*argv[1] == '-') {
  225.         /*
  226.          * most serial lines use about 10 bits per character (7 data, 1
  227.          *  parity, 1 start and 1 stop). Since I want milliseconds per
  228.          *  character, I divide 10000 by the baud rate.
  229.          */
  230.         g_speed = 10000 / atoi(&(argv[1][1]));
  231.  
  232.         /*
  233.          * On a standard PC, it already takes over 1 millisecond to
  234.          *  process a character, so adjust the rate accordingly.
  235.          */
  236.         if (g_speed > 0) {
  237.             --g_speed;
  238.         }
  239.  
  240.         /*
  241.          * remove the baud rate from the argument list
  242.          */
  243.         ++argv;
  244.         --argc;
  245.     }
  246.  
  247.     /*
  248.      * now start up the main editor
  249.      */
  250.     editor(argc, argv);
  251. }
  252.  
  253. /*
  254.  * The following defines specify which video attributes give desired
  255.  *  effects on different display devices.
  256.  * REVERSE is supposed to be reverse video - a different background color,
  257.  *  so that even a blank space can be identified.
  258.  * HIGH is supposed to quickly draw the user's eye to the relevant part of
  259.  *  the screen, either for a message or for matched text in find/replace.
  260.  * NORMAL is supposed to be something pleasant to look at for the main
  261.  *  body of the text.
  262.  * These defines may not be optimal for all types of display. Eventually
  263.  *  the user should be allowed to select which attribute is used where.
  264.  */
  265. #define LCD_REVERSE 0x70
  266. #define LCD_NORMAL  0x07
  267. #define LCD_HIGH    0x17
  268.  
  269. #define HERC_REVERSE 0x70
  270. #define HERC_UNDER   0x01
  271. #define HERC_NORMAL  0x07
  272. #define HERC_HIGH    0x0F
  273.  
  274. #define COLOR_NORMAL 0x07
  275. #define COLOR_REVERSE 0x17
  276. #define COLOR_HIGH 0x1F
  277.  
  278. /*
  279.  * Name:    sim_delay
  280.  * Purpose: To simulate the delay that would be caused by sending the
  281.  *           specified number of characters along a serial line.
  282.  * Passed:  n: the number of characters "transmitted"
  283.  * Date:    October 10, 1989
  284.  */
  285. static void sim_delay(n)
  286. int n;
  287. {
  288.     if (g_speed > 0) {
  289.         delay(g_speed*n);
  290.     }
  291. }
  292.  
  293. /*
  294.  * Name:    att_stuff
  295.  * Purpose: To make sure that the attribute is set to normal before commands
  296.  *           such as clear to end of line are executed.
  297.  * Date:    October 10, 1989
  298.  * Passed:  [g_display.attr]:   the current attribute
  299.  *          [g_display.normal]: the normal attribute
  300.  * Returns: [g_display.attr]:   set to normal
  301.  * Notes:   This function is necessary because some terminals clear to
  302.  *           spaces using the current attribute, while others clear to
  303.  *           normal spaces. Unfortunately terminfo does not seem to record
  304.  *           this distinction.
  305.  *          For the PC, this function is only used so we can simulate delays
  306.  *           on real terminals better.
  307.  */
  308. static void att_stuff()
  309. {
  310.     if (g_display.attr != g_display.normal) {
  311.         textattr(g_display.normal);
  312.         g_display.attr = g_display.normal;
  313.         sim_delay(2);  /* most attribute commands take 2 bytes */
  314.     }
  315. }
  316.  
  317. /*
  318.  * Name:    att_check
  319.  * Purpose: To check that the attribute required for the next character is
  320.  *           the one currently in effect, and set it if different.
  321.  * Date:    October 10, 1989
  322.  * Passed:  [g_display.attr]:  the current attribute
  323.  *          [g_status.wanted]: the required attribute
  324.  * Returns: [g_display.attr]:  the newly set attribute
  325.  */
  326. void att_check()
  327. {
  328.     if (g_display.attr != g_status.wanted) {
  329.         textattr(g_status.wanted);
  330.         g_display.attr = g_status.wanted;
  331.         sim_delay(2);
  332.     }
  333. }
  334.  
  335. /*
  336.  * Name:    hw_xygoto
  337.  * Purpose: To move the cursor to a new position on the screen.
  338.  * Date:    October 10, 1989
  339.  * Passed:  [g_display.line]: the required line
  340.  *          [g_display.col]:  the required column
  341.  */
  342. void hw_xygoto()
  343. {
  344.     gotoxy(g_display.col+1, g_display.line+1);
  345.     sim_delay(4);
  346. }
  347.  
  348. /*
  349.  * The following locally global variables are used to keep track of the
  350.  *  character in the bottom right corner of the screen.
  351.  * It is not safe to write this character, since most terminals will
  352.  *  scroll the whole screen up a line after writing it.
  353.  * However, if the screen is subsequently scrolled up for any reason, then
  354.  *  this character must appear on the second bottom line!
  355.  * This causes numerous complications in the code which follows...
  356.  */
  357. static char g_mem_c = 0;   /* character supposed to be at bottom right */
  358. static char g_mem_attr;    /* attribute for g_mem_c */
  359.  
  360. /*
  361.  * Name:    hw_clreol
  362.  * Purpose: To clear from the cursor to the end of the cursor line.
  363.  * Date:    October 10, 1989
  364.  * Returns: TRUE if the hardware could clear to end of line, FALSE otherwise
  365.  */
  366. int hw_clreol()
  367. {
  368.     /*
  369.      * clear to end of line, using normal attribute
  370.      */
  371.     att_stuff();
  372.     clreol();
  373.     sim_delay(2);
  374.  
  375.     /*
  376.      * If we just cleared the bottom line, then the bottom right character
  377.      *  was cleared too.
  378.      */
  379.     if (g_display.line == g_display.nlines-1) {
  380.         g_mem_c = 0;
  381.     }
  382.  
  383.     return TRUE;
  384. }
  385.  
  386. /*
  387.  * Name:    hw_linedel
  388.  * Purpose: To delete the cursor line, scrolling lines below up.
  389.  * Date:    October 10, 1989
  390.  * Passed:  line:  line on screen to be deleted
  391.  * Returns: TRUE if the hardware could delete the line, FALSE otherwise
  392.  */
  393. int hw_linedel(line)
  394. int line;
  395. {
  396.     /*
  397.      * delete the line
  398.      */
  399.     att_stuff();
  400.     xygoto(0, line);
  401.     delline();
  402.     sim_delay(2);
  403.  
  404.     /*
  405.      * If this caused the bottom line to move up (which will usually be
  406.      *  the case), then add the bottom right character (if any) onto the
  407.      *  second bottom line.
  408.      */
  409.     if (g_mem_c) {
  410.         if (line < g_display.nlines-1) {
  411.             xygoto(g_display.ncols-1, g_display.nlines-2);
  412.             if (g_display.attr != g_mem_attr) {
  413.                 textattr(g_mem_attr);
  414.                 g_display.attr = g_mem_attr;
  415.                 sim_delay(2);
  416.             }
  417.             putch(g_mem_c);
  418.         }
  419.         g_mem_c = 0;
  420.     }
  421.     return TRUE;
  422. }
  423.  
  424. /*
  425.  * Name:    hw_scroll_up
  426.  * Purpose: To scroll the lines in a given region up one line.
  427.  * Date:    October 10, 1989
  428.  * Passed:  top:    the top line in the window
  429.  *          bottom: the bottom line in the window
  430.  * Returns: TRUE if terminal could scroll, FALSE otherwise
  431.  * Notes:   If this function does not exist, then insert and delete line
  432.  *           can achieve the same effect. However, insert and delete line
  433.  *           make lower windows jump, so using terminal scrolling is
  434.  *           preferable.
  435.  */
  436. int hw_scroll_up(top, bottom)
  437. int top;
  438. int bottom;
  439. {
  440.     /*
  441.      * select window to be affected
  442.      */
  443.     att_stuff();
  444.     xygoto(0, top);
  445.     window(1, top+1, g_display.ncols, bottom+1);
  446.     g_display.col = -1;
  447.     g_display.line = -1;
  448.     sim_delay(4);
  449.  
  450.     /*
  451.      * scroll the window up
  452.      */
  453.     delline();
  454.     sim_delay(2);
  455.  
  456.     /*
  457.      * Turbo C does cursor movements in the current window, whereas most
  458.      *  terminals (even with windows set) still do cursor addressing on
  459.      *  the entire screen.
  460.      * Cursor position is usually undefined after window commands.
  461.      */
  462.     window(1, 1, g_display.ncols, g_display.nlines);
  463.     sim_delay(4);
  464.     g_display.line = -1;
  465.     g_display.col = -1;
  466.  
  467.     /*
  468.      * if the bottom line was scrolled up, then restore the old bottom
  469.      *  right character to the second bottom line
  470.      */
  471.     if (bottom == g_display.nlines-1 && g_mem_c) {
  472.         xygoto(g_display.ncols-1, g_display.nlines-2);
  473.         if (g_display.attr != g_mem_attr) {
  474.             textattr(g_mem_attr);
  475.             g_display.attr = g_mem_attr;
  476.             sim_delay(2);
  477.         }
  478.         putch(g_mem_c);
  479.         g_mem_c = 0;
  480.     }
  481.  
  482.     return TRUE;
  483. }
  484.  
  485. /*
  486.  * Name:    hw_lineins
  487.  * Purpose: To insert a blank line above the cursor line, scrolling the
  488.  *           cursor line and lines below down.
  489.  * Date:    October 10, 1989
  490.  * Passed:  line:  line on screen to be inserted
  491.  * Returns: TRUE if the hardware could insert the line, FALSE otherwise
  492.  */
  493. int hw_lineins(line)
  494. int line;
  495. {
  496.     /*
  497.      * insert the line
  498.      */
  499.     att_stuff();
  500.     xygoto(0, line);
  501.     insline();
  502.     sim_delay(2);
  503.  
  504.     /*
  505.      * regardless of where the line was inserted, the bottom line
  506.      *  (including the bottom right character) scrolled off the screen
  507.      */
  508.     g_mem_c = 0;
  509.  
  510.     return TRUE;
  511. }
  512.  
  513. /*
  514.  * Name:    hw_scroll_down
  515.  * Purpose: To scroll the lines in a given region down one line.
  516.  * Date:    October 10, 1989
  517.  * Passed:  top:    the top line in the window
  518.  *          bottom: the bottom line in the window
  519.  * Returns: TRUE if terminal could scroll, FALSE otherwise
  520.  * Notes:   If this function does not exist, then insert and delete line
  521.  *           can achieve the same effect. However, insert and delete line
  522.  *           make lower windows jump, so using terminal scrolling is
  523.  *           preferable.
  524.  */
  525. int hw_scroll_down(top, bottom)
  526. int top;
  527. int bottom;
  528. {
  529.     /*
  530.      * select region to be affected
  531.      */
  532.     att_stuff();
  533.     xygoto(0, top);
  534.     window(1, top+1, g_display.ncols, bottom+1);
  535.     g_display.col = -1;
  536.     g_display.line = -1;
  537.     sim_delay(4);
  538.  
  539.     /*
  540.      * scroll down
  541.      */
  542.     insline();
  543.     sim_delay(2);
  544.  
  545.     /*
  546.      * restore screen (see hw_scroll_up)
  547.      */
  548.     window(1, 1, g_display.ncols, g_display.nlines);
  549.     sim_delay(4);
  550.     g_display.line = -1;
  551.     g_display.col = -1;
  552.  
  553.     /*
  554.      * if the region included the bottom line, then the bottom right
  555.      *  character moved off the screen altogether
  556.      */
  557.     if (bottom == g_display.nlines-1) {
  558.         g_mem_c = 0;
  559.     }
  560.  
  561.     return TRUE;
  562. }
  563.  
  564. /*
  565.  * Name:    hw_c_avail
  566.  * Purpose: To test whether or not a character has been typed by the user.
  567.  * Date:    October 10, 1989
  568.  * Returns: TRUE if user typed something, FALSE otherwise
  569.  */
  570. int hw_c_avail()
  571. {
  572.     return bioskey(1);
  573. }
  574.  
  575. /*
  576.  * Name:    hw_c_input
  577.  * Purpose: To input a character from the user, without echo, waiting if
  578.  *           nothing has been typed yet.
  579.  * Date:    October 10, 1989
  580.  * Returns: the character the user typed
  581.  * Notes:   A return value of 0 means that what the user typed should be
  582.  *           ignored.
  583.  */
  584. int hw_c_input()
  585. {
  586.     int key;
  587.     screen_chars buff[2];
  588.  
  589.     buff[0].c = ((g_display.col+1) / 10) + '0';
  590.     buff[1].c = ((g_display.col+1) % 10) + '0';
  591.     buff[0].attr = buff[1].attr = g_display.block;
  592.     puttext(MAX_COLS-1, 1, MAX_COLS, 1, buff);
  593.  
  594.     key = bioskey(0);
  595.     if ((key & 0xFF) == 0) {
  596.         /*
  597.          * The user entered a function key. Translate it into the
  598.          *  appropriate command, or ignore.
  599.          */
  600.         if (key == 0x4700) { /* home */
  601.             c_uninput(CONTROL('S'));
  602.             return CONTROL('Q');
  603.         }
  604.         if (key == 0x4800) { /* up arrow */
  605.             return CONTROL('E');
  606.         }
  607.         if (key == 0x4900) { /* page up */
  608.             return CONTROL('R');
  609.         }
  610.         if (key == 0x4b00) { /* left arrow */
  611.             return CONTROL('S');
  612.         }
  613.         if (key == 0x4d00) { /* right arrow */
  614.             return CONTROL('D');
  615.         }
  616.         if (key == 0x4f00) { /* end */
  617.             c_uninput(CONTROL('D'));
  618.             return CONTROL('Q');
  619.         }
  620.         if (key == 0x5000) { /* down arrow */
  621.             return CONTROL('X');
  622.         }
  623.         if (key == 0x5100) { /* page down */
  624.             return CONTROL('C');
  625.         }
  626.         if (key == 0x5200) { /* insert */
  627.             return CONTROL('V');
  628.         }
  629.         if (key == 0x5300) { /* del */
  630.             return CONTROL('G');
  631.         }
  632.         if (key == 0x2d00) { /* AltX */
  633.             c_uninput(CONTROL('X'));
  634.             return CONTROL('K');
  635.         }
  636.         return 0;
  637.     }
  638.     else {
  639.         return key & 0xFF;
  640.     }
  641. }
  642.  
  643. /*
  644.  * Name:    hw_c_output
  645.  * Purpose: To output a character, using the current attribute, at the
  646.  *           current screen position.
  647.  * Date:    October 10, 1989
  648.  * Notes:   If the current screen position is the bottom right corner, then
  649.  *           we do not write the character, but merely store it away for
  650.  *           later. (See explanation above.)
  651.  */
  652. void hw_c_output(c)
  653. int c;
  654. {
  655.     if (g_display.line == g_display.nlines-1 &&
  656.             g_display.col == g_display.ncols-1) {
  657.         g_mem_c = c;
  658.         g_mem_attr = g_status.wanted;
  659.         return;
  660.     }
  661.     att_check();
  662.     putch(c);
  663.     sim_delay(1);
  664. }
  665.  
  666. /*
  667.  * Name:    hw_terminate
  668.  * Purpose: To restore the terminal to a safe state prior to leaving the
  669.  *           editor.
  670.  * Date:    October 10, 1989
  671.  */
  672. void hw_terminate()
  673. {
  674.     /*
  675.      * leave the editor text on the screen, but move the cursor to the
  676.      *  bottom line.
  677.      */
  678.     xygoto(0, g_display.nlines-1);
  679.     att_stuff();
  680.     putch('\n');
  681.     sim_delay(1);
  682.  
  683.     printf("dte version %s for IBM PC (with CGA)", VERSION);
  684.  
  685.     /*
  686.      * restore control-break checking
  687.      */
  688.     setcbrk(s_cbrk);
  689. }
  690.  
  691. /*
  692.  * Name:    hw_initialize
  693.  * Purpose: To initialize the display ready for editor use.
  694.  * Date:    October 10, 1989
  695.  */
  696. void hw_initialize()
  697. {
  698.     struct text_info buff; /* for discovering display type */
  699.     long space;            /* amount of memory to use */
  700.  
  701.     /*
  702.      * set up path name for help file
  703.      */
  704.     if (*g_status.help_file == '\0') {
  705.         strcpy(g_status.help_file, searchpath("dte.hlp"));
  706.     }
  707.  
  708.     /*
  709.      * set up screen size
  710.      */
  711.     g_display.ncols = MAX_COLS;
  712.     g_display.nlines = MAX_LINES;
  713.  
  714.     /*
  715.      * cursor addressing usually takes 4 bytes
  716.      */
  717.     g_display.ca_len = 4;
  718.  
  719.     /*
  720.      * allocate space for the screen image
  721.      */
  722.     if ((g_screen = (screen_lines *)malloc(MAX_LINES * sizeof(screen_lines)))
  723.             == NULL) {
  724.         error(FATAL, "out of memory???");
  725.     }
  726.  
  727.     /*
  728.      * use almost all the available memory for the text buffer, but
  729.      *  reserve some for opening files and windows and shelling to
  730.      *  DOS.
  731.      * If there is plenty of memory available, then try to preserve
  732.      *  command.com as well.
  733.      */
  734.     space = farcoreleft() - 50000L;
  735.     if (space < 100000L) {
  736.         space += 40000L;
  737.     }
  738.     if ((g_status.start_mem = farmalloc(space)) == NULL) {
  739.         error(FATAL, "out of memory???");
  740.     }
  741.     g_status.max_mem = g_status.start_mem + space;
  742.  
  743.     /*
  744.      * work out what kind of display is in use, and set attributes
  745.      *  accordingly.
  746.      */
  747.     gettextinfo(&buff);
  748.     if (buff.currmode == MONO) {
  749.         g_display.block = HERC_REVERSE;
  750.         g_display.normal = HERC_NORMAL;
  751.         g_display.flash = HERC_HIGH;
  752.         g_display.attr = HERC_NORMAL;
  753.     }
  754.     else {
  755.         if (buff.currmode == BW80) {
  756.             /*
  757.              * There are probably some machines apart from ones with liquid
  758.              *  crystal displays which use BW80 mode, in which case these
  759.              *  attributes may not be appropriate.
  760.              */
  761.             g_display.block = LCD_REVERSE;
  762.             g_display.normal = LCD_NORMAL;
  763.             g_display.flash = LCD_HIGH;
  764.             g_display.attr = LCD_NORMAL;
  765.         }
  766.         else {
  767.             g_display.block = COLOR_REVERSE;
  768.             g_display.normal = COLOR_NORMAL;
  769.             g_display.flash = COLOR_HIGH;
  770.             g_display.attr = COLOR_NORMAL;
  771.         }
  772.     }
  773. }
  774.  
  775. /*
  776.  * Name:    hw_move
  777.  * Purpose: To move data from one place to another as efficiently as
  778.  *           possible.
  779.  * Date:    October 10, 1989
  780.  * Passed:  dest:   where to copy to
  781.  *          source: where to copy from
  782.  *          number: number of bytes to copy
  783.  * Notes:   moves may be (usually will be) overlapped
  784.  */
  785. void hw_move(dest, source, number)
  786. text_ptr dest;
  787. text_ptr source;
  788. long number;
  789. {
  790.     if (number < 0) {
  791.         /*
  792.          * this should never happen...
  793.          */
  794.         error(WARNING, "negative move - contact Douglas Thomson");
  795.     }
  796.     else if (source == dest) {
  797.         /*
  798.          * nothing to be done
  799.          */
  800.         ;
  801.     }
  802.     else if (source > dest) {
  803.         /*
  804.          * Turbo C provides a move that can handle overlapping moves,
  805.          *  but unfortunately it can only move up to 64K-1 bytes.
  806.          * Since I could not move 64K, I have only tried to move 32K.
  807.          */
  808.         while (number > 0x8000L) {
  809.             memmove((char *)dest, (char *)source, 0x8000);
  810.             number -= 0x8000L;
  811.             dest += 0x8000L;
  812.             source += 0x8000L;
  813.         }
  814.         /*
  815.          * now less than 32K is left, so finish off the move
  816.          */
  817.         memmove((char *)dest, (char *)source, (unsigned)number);
  818.     }
  819.     else {
  820.         source += number;
  821.         dest += number;
  822.         while (number > 0x8000L) {
  823.             source -= 0x8000L;
  824.             dest -= 0x8000L;
  825.             number -= 0x8000L;
  826.             memmove((char *)dest, (char *)source, 0x8000);
  827.         }
  828.         source -= number;
  829.         dest -= number;
  830.         memmove((char *)dest, (char *)source, (unsigned)number);
  831.     }
  832. }
  833.  
  834. /*
  835.  * Name:    hw_backspace
  836.  * Purpose: To move the cursor left one position.
  837.  * Date:    October 10, 1989
  838.  * Returns: TRUE if the hardware could backspace, FALSE otherwise
  839.  * Notes:   This function is used where deletion requires a backspace,
  840.  *           space, backspace. If the terminal can backspace, this may
  841.  *           be much faster than using cursor addressing.
  842.  */
  843. int hw_backspace()
  844. {
  845.     gotoxy(g_display.col, g_display.line+1);
  846.     sim_delay(1);
  847.     return TRUE;
  848. }
  849.  
  850. /*
  851.  * Name:    hw_c_insert
  852.  * Purpose: To insert a blank character under the cursor.
  853.  * Date:    October 10, 1989
  854.  * Returns: TRUE if the hardware could insert the space, FALSE otherwise
  855.  * Notes:   This function is used where the user has just typed a character
  856.  *           in the middle of a line in insert mode. If it is available, it
  857.  *           saves having to redraw the entire remainder of the line.
  858.  *          No assumptions are made about the contents or attribute of the
  859.  *           inserted character.
  860.  */
  861. int hw_c_insert()
  862. {
  863.     movetext(g_display.col+1, g_display.line+1, g_display.ncols-1,
  864.             g_display.line+1, g_display.col+2, g_display.line+1);
  865.     sim_delay(2);
  866.     return TRUE;
  867. }
  868.  
  869. /*
  870.  * Name:    hw_c_delete
  871.  * Purpose: To delete the character under the cursor.
  872.  * Date:    October 10, 1989
  873.  * Returns: TRUE if the hardware could delete the character, FALSE otherwise
  874.  * Notes:   This function is used where the user has deleted a character
  875.  *           in the middle of a line. If it is available, it saves having to
  876.  *           redraw the entire remainder of the line.
  877.  *          The rightmost character on the line after the delete is assumed
  878.  *           to be a space character with normal attribute.
  879.  */
  880. int hw_c_delete()
  881. {
  882.     screen_chars blank;  /* for blanking rightmost character */
  883.  
  884.     movetext(g_display.col+2, g_display.line+1, g_display.ncols,
  885.             g_display.line+1, g_display.col+1, g_display.line+1);
  886.     sim_delay(2);
  887.  
  888.     /*
  889.      * make sure rightmost character is correct - this happens
  890.      *  automatically on most terminals, so no delay
  891.      */
  892.     blank.c = ' ';
  893.     blank.attr = g_display.normal;
  894.     puttext(g_display.ncols, g_display.line+1,
  895.             g_display.ncols, g_display.line+1, &blank);
  896.  
  897.     /*
  898.      * bottom right corner character could need to reappear one
  899.      *  character in from the right
  900.      */
  901.     if (g_mem_c && g_display.line == g_display.nlines-1) {
  902.         if (g_display.col < g_display.ncols-2) {
  903.             xygoto(g_display.ncols-2, g_display.nlines-1);
  904.             if (g_display.attr != g_mem_attr) {
  905.                 textattr(g_mem_attr);
  906.                 g_display.attr = g_mem_attr;
  907.                 sim_delay(2);
  908.             }
  909.             putch(g_mem_c);
  910.         }
  911.         g_mem_c = 0;
  912.     }
  913.  
  914.     return TRUE;
  915. }
  916.  
  917. /*
  918.  * Name:    hw_rename
  919.  * Purpose: To rename a disk file to a new name.
  920.  * Date:    October 10, 1989
  921.  * Passed:  old: current file name
  922.  *          new: new desired file name
  923.  * Returns: OK if rename succeeded, ERROR if any problem
  924.  */
  925. int hw_rename(old, new)
  926. char *old;
  927. char *new;
  928. {
  929.     return rename(old, new);
  930. }
  931.  
  932. /*
  933.  * Name:    hw_fattrib
  934.  * Purpose: To determine the current file attributes.
  935.  * Date:    October 17, 1989
  936.  * Passed:  name: name of file to be checked
  937.  * Returns: current read/write/execute etc attributes of the file, or
  938.  *          ERROR if file did not exist etc.
  939.  */
  940. int hw_fattrib(name)
  941. char *name;
  942. {
  943.     return _chmod(name, 0);
  944. }
  945.  
  946. /*
  947.  * Name:    hw_set_fattrib
  948.  * Purpose: To set the current file attributes.
  949.  * Date:    October 17, 1989
  950.  * Passed:  name:   name of file to be changed
  951.  *          attrib: the required attributes
  952.  * Returns: new read/write/execute etc attributes of the file, or
  953.  *          ERROR if file did not exist etc.
  954.  * Notes:   If "attrib" is ERROR, then do not change attributes.
  955.  */
  956. int hw_set_fattrib(name, attrib)
  957. char *name;
  958. int attrib;
  959. {
  960.     if (attrib == ERROR) {
  961.         return ERROR;
  962.     }
  963.     return _chmod(name, 1, attrib);
  964. }
  965.  
  966. /*
  967.  * Name:    hw_unlink
  968.  * Purpose: To delete a file, regardless of access modes.
  969.  * Date:    October 17, 1989
  970.  * Passed:  name:   name of file to be removed
  971.  * Returns: OK if file could be removed
  972.  *          ERROR otherwise
  973.  */
  974. int hw_unlink(name)
  975. char *name;
  976. {
  977.     int result;
  978.  
  979.     if ((result = _chmod(name, 0)) != -1 && (result & FA_RDONLY) != 0) {
  980.         /*
  981.          * file cannot be written
  982.          */
  983.         set_prompt("File is write protected! Overwrite anyway? (y/n): ", 1);
  984.         if (display(get_yn, 1) != A_YES) {
  985.             return ERROR;
  986.         }
  987.         if (_chmod(name, 1, 0) == ERROR) {
  988.             return ERROR;
  989.         }
  990.     }
  991.     return unlink(name);
  992. }
  993.  
  994. /*
  995.  * Name:    hw_printable
  996.  * Purpose: To determine whether or not a character is printable on the
  997.  *           current hardware.
  998.  * Date:    October 18, 1989
  999.  * Passed:  c: the character to be tested
  1000.  * Returns: TRUE if c is a visible character, FALSE otherwise
  1001.  * Notes:   This is hardware dependent so that machines like the IBM PC can
  1002.  *           edit files containing graphics characters.
  1003.  */
  1004. int hw_printable(c)
  1005. int c;
  1006. {
  1007.    return (c >= 32);
  1008. }
  1009.  
  1010. /*
  1011.  * Name:    hw_load
  1012.  * Purpose: To load a file into the text buffer.
  1013.  * Date:    November 11, 1989
  1014.  * Passed:  name:  name of disk file
  1015.  *          start: first character in text buffer
  1016.  *          limit: last available character in text buffer
  1017.  *          end:   last character (+1) from the file
  1018.  * Returns: OK, or ERROR if anything went wrong
  1019.  * Notes:   All error messages are displayed here, so the caller should
  1020.  *           neither tell the user what is happening, nor print an error
  1021.  *           message if anything goes wrong.
  1022.  *          This function is in the hardware dependent module because
  1023.  *           some computers require non-standard open parameters...
  1024.  */
  1025. int hw_load(name, start, limit, end)
  1026. char *name;
  1027. text_ptr start;
  1028. text_ptr limit;
  1029. text_ptr *end;
  1030. {
  1031.     int fd;         /* file being read */
  1032.     int length;     /* number of bytes actually read */
  1033.  
  1034.     /*
  1035.      * try reading the file
  1036.      */
  1037.     if ((fd = open(name, O_RDONLY)) == ERROR) {
  1038.         error(WARNING, "File '%s' not found", name);
  1039.         return ERROR;
  1040.     }
  1041.  
  1042.     /*
  1043.      * tell the user what is happening
  1044.      */
  1045.     error(TEMP, "Reading file '%s'...", name);
  1046.  
  1047.     /*
  1048.      * read the entire file, without going past end of buffer.
  1049.      * Note that this means a file that is within 1K of the limit
  1050.      *  will not be accepted.
  1051.      */
  1052.     limit -= 1024;
  1053.     for (;;) {
  1054.         if (start >= limit) {
  1055.             error(WARNING, "file '%s' too big", name);
  1056.             close(fd);
  1057.             return ERROR;
  1058.         }
  1059.         if ((length = read(fd, (char *)start, 1024)) == ERROR) {
  1060.             error(WARNING, "could not read file '%s'", name);
  1061.             close(fd);
  1062.             return ERROR;
  1063.         }
  1064.         start += length;
  1065.         if (length == 0) {
  1066.             /*
  1067.              * we reached the end of file
  1068.              */
  1069.             break;
  1070.         }
  1071.     }
  1072.  
  1073.     /*
  1074.      * close the file and report the final character in the buffer
  1075.      */
  1076.     close(fd);
  1077.     *end = start;
  1078.  
  1079.     return OK;
  1080. }
  1081.  
  1082. /*
  1083.  * Name:    write_file
  1084.  * Purpose: To write text to a file, eliminating trailing space on the
  1085.  *           way.
  1086.  * Date:    November 11, 1989
  1087.  * Passed:  name:  name of disk file or device
  1088.  *          mode:  fopen flags to be used in open
  1089.  *          start: first character in text buffer
  1090.  *          end:   last character (+1) in text buffer
  1091.  * Returns: OK, or ERROR if anything went wrong
  1092.  * Notes:   Trailing space at the very end of the text is NOT removed,
  1093.  *           so that a block write of a block of spaces will work.
  1094.  *          No error messages are displayed here, so the caller must
  1095.  *           both tell the user what is happening, and print an error
  1096.  *           message if anything goes wrong.
  1097.  *          This function is in the hardware dependent module because
  1098.  *           some computers require non-standard open parameters...
  1099.  */
  1100. static int write_file(name, mode, start, end)
  1101. char *name;
  1102. char *mode;
  1103. text_ptr start;
  1104. text_ptr end;
  1105. {
  1106.     FILE *fp;       /* file to be written */
  1107.     int spaces;     /* no. of space characters pending */
  1108.     char c;         /* current character in file */
  1109.  
  1110.     /*
  1111.      * create a new file, or truncate an old one
  1112.      */
  1113.     if ((fp = fopen(name, mode)) == NULL) {
  1114.         return ERROR;
  1115.     }
  1116.  
  1117.     /*
  1118.      * save the file, eliminating trailing space
  1119.      */
  1120.     spaces = 0;
  1121.     for (;;) {
  1122.         if (start == end) {
  1123.             break;
  1124.         }
  1125.         if ((c = *start++) == ' ') {
  1126.             spaces++;   /* count them, maybe output later */
  1127.             continue;
  1128.         }
  1129.  
  1130.         if (c == '\n') {
  1131.             spaces = 0; /* eliminate the trailing space */
  1132.         }
  1133.         else if (spaces) {
  1134.             /*
  1135.              * the spaces were NOT trailing, so output them now
  1136.              */
  1137.             do {
  1138.                 if (putc(' ', fp) == ERROR) {
  1139.                     fclose(fp);
  1140.                     return ERROR;
  1141.                 }
  1142.             } while (--spaces);
  1143.         }
  1144.  
  1145.         if (putc(c, fp) == ERROR) {
  1146.             fclose(fp);
  1147.             return ERROR;
  1148.         }
  1149.     }
  1150.  
  1151.     /*
  1152.      * output any trailing space at end of file - this may be important
  1153.      *  for block writes.
  1154.      */
  1155.     if (spaces) {
  1156.         do {
  1157.             if (putc(' ', fp) == ERROR) {
  1158.                 fclose(fp);
  1159.                 return ERROR;
  1160.             }
  1161.         } while (--spaces);
  1162.     }
  1163.  
  1164.     return fclose(fp);
  1165. }
  1166.  
  1167. /*
  1168.  * Name:    hw_save
  1169.  * Purpose: To save text to a file, eliminating trailing space on the
  1170.  *           way.
  1171.  * Date:    November 11, 1989
  1172.  * Passed:  name:  name of disk file
  1173.  *          start: first character in text buffer
  1174.  *          end:   last character (+1) in text buffer
  1175.  * Returns: OK, or ERROR if anything went wrong
  1176.  * Notes:   Trailing space at the very end of the file is NOT removed,
  1177.  *           so that a block write of a block of spaces will work.
  1178.  *          No error messages are displayed here, so the caller must
  1179.  *           both tell the user what is happening, and print an error
  1180.  *           message if anything goes wrong.
  1181.  *          This function is in the hardware dependent module because
  1182.  *           some computers require non-standard open parameters...
  1183.  */
  1184. int hw_save(name, start, end)
  1185. char *name;
  1186. text_ptr start;
  1187. text_ptr end;
  1188. {
  1189.     return write_file(name, "w", start, end);
  1190. }
  1191.  
  1192. /*
  1193.  * Name:    hw_append
  1194.  * Purpose: To append text to a file.
  1195.  * Date:    November 11, 1989
  1196.  * Passed:  name:  name of disk file
  1197.  *          start: first character in text buffer
  1198.  *          end:   last character (+1) in text buffer
  1199.  * Returns: OK, or ERROR if anything went wrong
  1200.  * Notes:   No error messages are displayed here, so the caller must
  1201.  *           both tell the user what is happening, and print an error
  1202.  *           message if anything goes wrong.
  1203.  *          This function is in the hardware dependent module because
  1204.  *           some computers require non-standard open parameters...
  1205.  */
  1206. int hw_append(name, start, end)
  1207. char *name;
  1208. text_ptr start;
  1209. text_ptr end;
  1210. {
  1211.     return write_file(name, "a", start, end);
  1212. }
  1213.  
  1214. /*
  1215.  * Name:    hw_print
  1216.  * Purpose: To print text to a printer.
  1217.  * Date:    November 11, 1989
  1218.  * Passed:  start: first character in text buffer
  1219.  *          end:   last character (+1) in text buffer
  1220.  * Returns: OK, or ERROR if anything went wrong
  1221.  * Notes:   This function is in the hardware dependent module because
  1222.  *           some computers require non-standard open parameters...
  1223.  */
  1224. int hw_print(start, end)
  1225. text_ptr start;
  1226. text_ptr end;
  1227. {
  1228.     return write_file("PRN", "a", start, end);
  1229. }
  1230.  
  1231. /*
  1232.  * Name:    hw_copy_path
  1233.  * Purpose: To create a new file path using most of an old path but
  1234.  *           changing just the file name.
  1235.  * Date:    November 8, 1989
  1236.  * Passed:  old:   the file path to extract path info from
  1237.  *          name:  the file name to add to the extracted path info
  1238.  * Returns: new:   the new path
  1239.  * Notes:   The file is located in the same place as the original, so
  1240.  *           that related editor files stay in the same directory.
  1241.  *          This function is hardware dependent because different characters
  1242.  *           delimit directories on different systems.
  1243.  */
  1244. void hw_copy_path(old, name, new)
  1245. char *old;
  1246. char *name;
  1247. char *new;
  1248. {
  1249.     char *cp;           /* cutoff point in old path */
  1250.  
  1251.     strcpy(new, old);
  1252.     if ((cp = strrchr(new, '/')) != NULL ||
  1253.             (cp = strrchr(new, '\\')) != NULL ||
  1254.             (cp = strrchr(new, ':')) != NULL) {
  1255.         ++cp;
  1256.     }
  1257.     else {
  1258.         cp = new;
  1259.     }
  1260.  
  1261.     strcpy(cp, name);
  1262. }
  1263.  
  1264. /*
  1265.  * Name:    hw_os_shell
  1266.  * Purpose: To shell out of the editor into the operating system, in such a
  1267.  *           way that editing may be resumed later.
  1268.  * Date:    November 28, 1990
  1269.  * Returns: TRUE if screen may have been clobbered, FALSE if screen OK.
  1270.  */
  1271. int hw_os_shell()
  1272. {
  1273.     static unsigned char ci[MAXPATH];
  1274.     static unsigned char dos_prompt[80];
  1275.     if (ci[0] == '\0') {
  1276.         strcpy(ci, getenv("COMSPEC"));
  1277.     }
  1278.     if (dos_prompt[0] == '\0') {
  1279.         sprintf(dos_prompt, "PROMPT=[DTE] %s", getenv("PROMPT"));
  1280.     }
  1281.     putenv(dos_prompt);
  1282.     spawnl(P_WAIT, ci, ci, 0);
  1283.     return TRUE;
  1284. }
  1285.